# @Time : 2024-11-29 12:24
# @Author : Fioman 
# @Phone : 13149920693
"""
程序的主函数入口,负责主程序的启动
"""
import json
import sys

import bottle
import bottle.ext.websocket as wbs
import gevent

import app_config
from app.camera_tool import CameraTool
from app.produce_handler import ProduceHandler
from app.state_listener import StateListener
from app.userinfo_manager import UserinfoManager
from common.datetime_helper import get_now_with_ms
from common.exception_helper import show_error_info
from common.exe_check import kill_old_instances
from common.produce_logger import debug_produce
from db_tool.db_handler import DbHandler
from drivers.cut_plc import CutPlc
from drivers.line_camera import LineCamera
from eap_control.eap_config import EapConfig
from eap_control.eap_handler import EapHandler
from eap_control.mock_request import MockRequest
from params.robot_params import RobotParams
from personal_tool.data_result import DataResult
from test_tool.test_handler import TestHandler
from web_tool.produce_data_model import ProduceDataType
from web_tool.send_to_web_handler import update_produce_model_in_web
from web_tool.web_handler import start_web_service, repeat_send


@bottle.route('/<path:path>')
def _static(path):
    """
    这是一个静态文件处理函数，使用 bottle.static_file 方法来处理来自客户端的静态文件请求。
    它的作用是根据提供的 path 参数从指定的根目录（app_config.web_path）中返回文件。
    例如，如果客户端请求 /images/logo.png，而 app_config.web_path 指向 /web/，
    那么 _static 函数将尝试返回 /web/images/logo.png 这个文件。
    静态文件路由 (/<path:path>) 是一个通配符路由，匹配所有的路径，包括 /service/api 和其他静态文件路径。
    """
    # debug_produce(f"_static 收到请求: {path}")
    return bottle.static_file(path, root=app_config.webPath)


@bottle.get('/eel', apply=[wbs.websocket])
def _websocket(ws):
    """
    这行代码使用 @bottle.route 装饰器定义了一个新的路由 /service/api。
    apply=[wbs.websocket] 指定了这个路由的处理方式是 WebSocket 连接。
    HTTP 请求: 每次客户端向服务器发送请求时，都会触发相应的路由函数。当请求处理完毕后，连接会关闭。
    这意味着如果需要再次请求相同的 URL，客户端会重新建立连接，服务器会再次调用路由处理函数。

    WebSocket 连接: 一旦 WebSocket 连接建立，它是持久的。客户端和服务器之间可以进行双向通信，
    而无需为每个消息重新建立连接。
    当客户端通过 /service/api 路由发起 WebSocket 连接时，_websocket(ws) 函数被调用。
    这个函数会处理连接的建立并进入一个循环。
    :param ws: WebSocket
    :return:
    """
    global webIsConnected
    page = bottle.request.query.page
    app_config.websockets += [(page, ws)]
    debug_produce(f"收到web连接请求: ({page},{ws}),时间={get_now_with_ms()}")
    webIsConnected = True
    try:
        while True:
            msg = ws.receive()
            print(f"recv: {msg}")
            if msg is not None:
                message = json.loads(msg)
                gevent.spawn(_process_message, message)
            else:
                debug_produce(f"收到web断开请求: ({page},{ws})")
                if (page, ws) in app_config.websockets:
                    app_config.websockets.remove((page, ws))
    except Exception as e:
        debug_produce(f"in_websocket() 异常: {e}")
        webIsConnected = False
        if (page, ws) in app_config.websockets:
            app_config.websockets.remove((page, ws))
        gevent.spawn(check_web_is_connect)


def check_web_is_connect():
    """
    检测web是否进行了重连
    :return:
    """
    waitTime = 2.5
    gevent.sleep(waitTime)
    if not webIsConnected:
        debug_produce(f"{waitTime} 秒内未检测到web连接,web网页关闭,后台执行关闭流程")
        _stop_program()
    else:
        debug_produce(f"{waitTime} 秒内检测到web重连,页面刷新")
        app_config.deviceLock = False
        update_produce_model_in_web(ProduceDataType.workingOrder)
        update_produce_model_in_web(ProduceDataType.totalOrders)


def _stop_program():
    """
    程序停止
    :return:
    """
    gevent.sleep(1)
    app_config.workStopState = True
    app_config.isMainAlive = False
    sys.exit(0)


def _process_message(message):
    """
    解析UI发送的消息,并且调用后台python中对象对应的函数,并且将结果返回
    :param message:前端发送过来的消息内容
    :return:
    """
    if 'call' in message:
        obj_name = message.get('objName', False)
        fun_name = message.get('funcName', False)
        if obj_name and fun_name:
            if obj_name == 'global':
                if globals().get(fun_name):
                    try:
                        return_val = globals()[fun_name](*message['args'])
                    except Exception as e:
                        return_val = "Call error!\n" + str(e)
                else:
                    return_val = "Call error!Global does not have %s function!" % fun_name
            else:
                if globals().get(obj_name):
                    if fun_name in dir(globals()[obj_name]):
                        if hasattr(getattr(globals()[obj_name], fun_name), '__call__'):
                            try:
                                return_val = getattr(globals()[obj_name], fun_name)(*message['args'])
                            except Exception as e:
                                return_val = "Call error!\n" + str(e)
                        else:
                            return_val = "Call error!%s 's %s is not a function!" % (obj_name, fun_name)
                            debug_produce(return_val)
                    else:
                        return_val = "Call error!%s does not have this %s!" % (obj_name, fun_name)
                        debug_produce(return_val)
                else:
                    return_val = "Call error!Global does not have %s object!" % obj_name
                    debug_produce(return_val)
        else:
            return_val = "Call command syntax error!"
            debug_produce(return_val)
        # 如果是自定义的DataResult类型,转换为字典,不然没有办法dumps
        if isinstance(return_val, DataResult):
            return_val = {
                "state": return_val.state,
                "data": return_val.data,
                "info": return_val.info
            }
        # 如果返回值为None,就不返回了.
        if return_val is not None:
            repeat_send(json.dumps({'return': message['call'], 'value': return_val}))
    else:
        debug_produce(f"Invalid message received: {message}")


if __name__ == '__main__':
    # 启动程序之前,先去检查,程序是否已经启动,如果已经启动,提醒用户程序已经启动,是否关闭重新打开
    if not app_config.isDevelop:
        kill_old_instances()
    webIsConnected = False
    ec = EapConfig()
    mr = MockRequest()
    db = DbHandler()
    rp = RobotParams()
    ipsResult = db.get_camera_ips()
    app_config.isMainAlive = True
    if not ipsResult.state:
        show_error_info(ipsResult.info)
        sys.exit(0)
    leftCamIp, rightCamIp = ipsResult.data
    leftCam = LineCamera(*leftCamIp)
    rightCam = LineCamera(*rightCamIp)
    cutPlc = CutPlc()
    um = UserinfoManager()
    st = StateListener(cutPlc, leftCam, rightCam)
    st.start_listeners()
    eapHandler = EapHandler()
    eapHandler.thread_start()
    cameraTool = CameraTool(leftCam, rightCam)
    produce = ProduceHandler(cameraTool, cutPlc)
    # 这里就是测试的时候使用,平时生产的时候不要使用
    testHdr = TestHandler(produce)
    debug_produce("程序启动成功.")
    start_web_service()
    debug_produce("程序已经正常退出")
